﻿using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Threading.Tasks;
using SDCloudSolution.SDCloud.DataLayer.Model.Interfaces;
using SDCloudSolution.SDCloud.Entities;
using SDCloudSolution.SDCloud.Entities.Models;

namespace SDCloudSolution.SDCloud.DataLayer.Model
{
    public class ModelDataProvider : IModelDataProvider
    {
        private readonly ModelDbContext _modelDbContext;

        public ModelDataProvider(ModelDbContext modelDbContext)
        {
            _modelDbContext = modelDbContext;
        }

        protected IDbSet<ModelEntity> Models
        {
            get { return _modelDbContext.Models; }
        }

        /// <summary>
        /// Gets a model entity by identifier.
        /// </summary>
        /// <param name="modelId">The model identifier.</param>
        public Task<ModelEntity> GetByIdAsync(int modelId)
        {
            return Models.FirstOrDefaultAsync(model => model.Id == modelId);
        }

        /// <summary>
        /// Gets a list of all models by specific user identifier.
        /// </summary>
        /// <param name="userId">The user identifier.</param>
        public async Task<IEnumerable<ModelEntity>> GetByUserIdAsync(int userId)
        {
            return await Models.Where(model => model.UserId == userId).ToListAsync();
        }

        /// <summary>
        /// Gets the list of all models that related to specific public model asynchronous.
        /// </summary>
        /// <param name="publicModelId">The public model identifier.</param>
        public async Task<IEnumerable<ModelEntity>> GetByPublicModelIdAsync(int publicModelId)
        {
            return await Models.Where(model => !model.IsPublic && model.OriginalModelId.HasValue && model.OriginalModelId.Value == publicModelId).ToListAsync();
        }

        /// <summary>
        /// Gets a collection with paging of models by specific user identifier.
        /// </summary>
        /// <param name="userId">The user identifier.</param>
        /// <param name="paging">The paging.</param>
        public Task<EntityCollection<ModelEntity>> GetByUserIdAsync(int userId, Paging paging)
        {
            return Models
                .Where(model => model.UserId == userId)
                .OrderByDescending(model => model.ModifyDateUtc ?? model.CreatedDateUtc)
                .ToEntityCollectionAsync(paging);
        }

        /// <summary>
        /// Gets the public models asynchronous.
        /// </summary>
        /// <param name="paging">The paging.</param>
        public Task<EntityCollection<ModelEntity>> GetPublicModelsAsync(Paging paging)
        {
            return Models
                .Where(model => model.IsPublic)
                .OrderByDescending(model => model.ModifyDateUtc ?? model.CreatedDateUtc)
                .ToEntityCollectionAsync(paging);
        }

        /// <summary>
        /// Gets the count of model copies for specific user.
        /// </summary>
        /// <param name="originalModelId">The original model identifier.</param>
        /// <param name="userId">The user identifier.</param>
        public Task<int> GetCountOfModelCopiesForUser(int originalModelId, int userId)
        {
            return Models
                .CountAsync(model => model.OriginalModelId == originalModelId && model.UserId == userId);
        }

        /// <summary>
        /// Gets the count of model copies.
        /// </summary>
        /// <param name="originalModelId">The original model identifier.</param>
        public Task<int> GetCountOfModelCopies(int originalModelId)
        {
            return Models
                .CountAsync(model => model.OriginalModelId == originalModelId);
        }

        /// <summary>
        /// Insert a new model entity.
        /// </summary>
        /// <param name="entity">The entity.</param>
        public Task InsertAsync(ModelEntity entity)
        {
            Models.Add(entity);
            return _modelDbContext.SaveChangesAsync();
        }

        /// <summary>
        /// Updates an exists model entity.
        /// </summary>
        /// <param name="entity">The entity.</param>
        public Task UpdateAsync(ModelEntity entity)
        {
            _modelDbContext.Models.Attach(entity);
            _modelDbContext.Entry(entity).State = EntityState.Modified;
            return _modelDbContext.SaveChangesAsync();
        }

        public Task DeleteAsync(ModelEntity entity)
        {
            _modelDbContext.Models.Remove(entity);
            return _modelDbContext.SaveChangesAsync();
        }
    }
}
